package com.mall.mymall.utils;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.mall.mymall.dao.entity.Products;
import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.action.DocWriteResponse;
import org.elasticsearch.action.admin.indices.create.CreateIndexRequest;
import org.elasticsearch.action.admin.indices.create.CreateIndexResponse;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.bulk.BulkResponse;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.delete.DeleteResponse;
import org.elasticsearch.action.get.GetRequest;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.action.update.UpdateResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.xcontent.XContentType;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.FieldType;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.lang.reflect.Field;
import java.util.List;
import java.util.Map;

/**
 * products--es具包
 */
// 被spring容器管理
@Component
@Slf4j
public class ProductsElasticSearchUtil {

    // 得到es客户端
    @Autowired
    private RestHighLevelClient restHighLevelClient;

    // 通过反射 得到ID
    public String getObjectId(Object data) {
        String idValue = null;
        try {
            String idName = "id"; // 默认字段名
            Field[] declaredFields = data.getClass().getDeclaredFields();
            for (Field field : declaredFields) {
                Id annotation = field.getAnnotation(Id.class);
                if (annotation != null) {
                    idName = field.getName(); // 获取带有 @Id 注解的字段名
                    break;
                }
            }
            Field declaredField = data.getClass().getDeclaredField(idName);
            declaredField.setAccessible(true); // 设置私有字段可访问
            Object fieldValue = declaredField.get(data); // 获取字段值
            if (fieldValue == null) {
                throw new RuntimeException("ID 字段值为 null，无法获取 ID");
            }

            idValue = fieldValue.toString(); // 转换为字符串
            log.info(" >>>>>> idValue: {}, fieldName: {}", idValue, idName);
        } catch (Exception e) {
            log.error("获取 ID 失败，原因: {}", e.getMessage(), e);
            throw new RuntimeException("获取 ID 失败，原因: " + e.getMessage(), e);
        }
        return idValue;
    }

    // 创建索引
    public String createIndex(Object data) throws Exception {

        // 这里是反射得到基础索引配置 先不用
//        DocumentIndex annotation = data.getClass().getAnnotation(DocumentIndex.class);
//        String indexName = annotation.indexName();
//        if (isIndexExist(indexName)) return indexName;
        // 手动设置基础索引配置
        String indexName = "products";
        // 最大体积
        Integer maxSize = 10000;
        // 分片数量
        Integer shards = 3;
        // 副本个数
        Integer replicas = 2;
        CreateIndexRequest request = new CreateIndexRequest(indexName);
        Settings.Builder builder = Settings.builder()
                .put("index.max_result_window", maxSize)
                .put("index.number_of_shards", shards)
                .put("index.number_of_replicas", replicas);
        request.settings(builder);

        JSONObject mapping = new JSONObject();
        JSONObject props = new JSONObject();
        mapping.put("properties", props);

        Field[] declaredFields = data.getClass().getDeclaredFields();
        for (Field field : declaredFields) {
            org.springframework.data.elasticsearch.annotations.Field esField
                    = field.getAnnotation(org.springframework.data.elasticsearch.annotations.Field.class);
            if (esField != null) {
                JSONObject prop = new JSONObject();
                String name = esField.name().isEmpty() ? field.getName() : esField.name();
                props.put(name, prop);
                prop.put("type", esField.type());
//                prop.put("index", propIndex.index());
                if (FieldType.Text.equals(esField.type())) {
                    // 这里是指定索引阶段的分词方式
                    prop.put("analyzer", "ik_max_word");
                    // 这里是指定查询阶段的分词方式
                    prop.put("search_analyzer", "ik_smart");
                }
            }
        }
        request.mapping("_doc", mapping.toJSONString(), XContentType.JSON);
        CreateIndexResponse response = restHighLevelClient.indices().create(request, RequestOptions.DEFAULT);
        return indexName;
    }

    public String createIndex2(Object data) throws Exception {
        // 手动设置基础索引配置
        String indexName = "products"; // 索引名称
        Integer maxSize = 10000; // 索引的最大结果窗口大小
        Integer shards = 3; // 索引的分片数量
        Integer replicas = 2; // 索引的副本数量

        try {
            // 生成字段映射
            JSONObject props = generateFieldMappings(data);
            JSONObject mapping = new JSONObject();
            mapping.put("properties", props);

            // 构建创建索引请求
            CreateIndexRequest request = buildCreateIndexRequest(indexName, mapping, maxSize, shards, replicas);

            try {
                // 发送创建索引请求
                CreateIndexResponse response = restHighLevelClient.indices().create(request, RequestOptions.DEFAULT);
                if (response.isAcknowledged()) {
                    log.info("Index created successfully: " + indexName); // 索引创建成功
                } else {
                    log.info("Index creation failed: " + indexName); // 索引创建失败
                }
            } catch (IOException e) {
                log.error("Error creating index: " + e.getMessage(), e); // 记录IO异常
                throw new IOException("Error creating index", e);
            } catch (ElasticsearchException e) {
                if (e.status().getStatus() == 400 && e.getMessage().contains("index_already_exists_exception")) {
                    log.info("Index already exists: " + indexName); // 索引已存在
                } else {
                    log.error("Elasticsearch error: " + e.getMessage(), e); // 记录Elasticsearch异常
                    throw new ElasticsearchException("Elasticsearch error", e);
                }
            }
        } catch (ReflectiveOperationException e) {
            log.error("Error in reflection: " + e.getMessage(), e); // 记录反射异常
            throw new IllegalArgumentException("Error in reflection", e);
        } catch (JsonProcessingException e) {
            log.error("Error in JSON processing: " + e.getMessage(), e); // 记录JSON处理异常
            throw new IllegalArgumentException("Error in JSON processing", e);
        }

        return indexName; // 返回索引名称
    }

    private JSONObject generateFieldMappings(Object data) throws ReflectiveOperationException {
        // 生成字段映射
        JSONObject props = new JSONObject();
        Field[] declaredFields = data.getClass().getDeclaredFields(); // 获取对象的所有字段
        for (Field field : declaredFields) {
            org.springframework.data.elasticsearch.annotations.Field esField = field.getAnnotation(org.springframework.data.elasticsearch.annotations.Field.class);
            if (esField != null) { // 如果字段有Elasticsearch注解
                JSONObject prop = new JSONObject();
                String name = esField.name().isEmpty() ? field.getName() : esField.name(); // 获取字段名称
                props.put(name, prop);
                prop.put("type", esField.type()); // 设置字段类型
                if (FieldType.Text.equals(esField.type())) { // 如果是文本类型字段
                    prop.put("analyzer", esField.analyzer() != null ? esField.analyzer() : "ik_max_word"); // 设置分词器
                    prop.put("search_analyzer", esField.searchAnalyzer() != null ? esField.searchAnalyzer() : "ik_smart"); // 设置搜索分词器
                }
            }
        }
        return props;
    }

    private CreateIndexRequest buildCreateIndexRequest(String indexName, JSONObject mapping, Integer maxSize, Integer shards, Integer replicas) {
        // 构建创建索引请求
        CreateIndexRequest request = new CreateIndexRequest(indexName); // 创建索引请求对象
        Settings.Builder builder = Settings.builder() // 构建索引设置
                .put("index.max_result_window", maxSize) // 设置最大结果窗口大小
                .put("index.number_of_shards", shards) // 设置分片数量
                .put("index.number_of_replicas", replicas); // 设置副本数量
        request.settings(builder); // 设置索引设置
        request.mapping("_doc", mapping.toJSONString(), XContentType.JSON); // 设置字段映射
        return request;
    }

    /**
     * 插入单个数据
     *
     * @param data 插入数据
     * @return 返回布尔类型
     */
    public boolean insertData(Object data) {
        String indexName = "products";
        String id = getObjectId(data);
        try {
            IndexRequest request = new IndexRequest(indexName)
                    .id(id)
                    .source(JSON.toJSONString(data), XContentType.JSON);
            IndexResponse response = restHighLevelClient.index(request, RequestOptions.DEFAULT);
            return handleDocWriteResponse(id, response);
        } catch (Exception e) {
            log.info(" >>>>>>> insertData error: {}", e.getMessage());
            e.printStackTrace();
        }
        return true;
    }

    /**
     * 批量插入数据
     *
     * @param products 插入数据集
     * @return 操作是否成功
     */
    public boolean batchInsert(List<Products> products) {
        if (CollectionUtils.isEmpty(products)) return false;
        String indexName = "products";
        try {
            // 创建BulkRequest对象，用于批量操作
            BulkRequest bulkRequest = new BulkRequest();

            // 遍历数据列表，为每条数据创建IndexRequest并添加到BulkRequest中
            products.forEach(data -> {
                // 获取数据的唯一ID
                String id = getObjectId(data);

                // 创建IndexRequest，指定索引名称、ID和数据内容
                IndexRequest indexRequest = new IndexRequest(indexName);

                // 检查文档是否已存在
                try {
                    GetRequest getRequest = new GetRequest("products", data.getProductId().toString());
                    GetResponse getResponse = restHighLevelClient.get(getRequest, RequestOptions.DEFAULT);
                    if (!getResponse.isExists()) {
                        // 文档不存在，添加到批量请求中
                        indexRequest.source(JSON.toJSONString(data), XContentType.JSON);
                        bulkRequest.add(indexRequest);
                    } else {
                        // 文档已存在，检查数据是否变化
                        Products existingProduct = handleResponseSourceToEntity(getResponse);

                        if (productsEqual(existingProduct, data)) {
                            // 数据有变化，更新文档
                            String json = JSON.toJSONString(data);
                            System.out.println(json);
                            UpdateRequest updateRequest = new UpdateRequest("products", data.getProductId().toString())
                                    .doc(json, XContentType.JSON)
                                    .upsert(json); // 如果文档不存在，创建新文档
                            bulkRequest.add(updateRequest);
                        }
                    }
                } catch (Exception e) {
                    log.error("Error preparing index request for product: " + data.getProductId(), e);
                }
            });
            if (bulkRequest.numberOfActions() == 0) {
                log.info("No grass submitted tasks");
                log.debug("No grass submitted tasks");
                return true;
            }
            // 执行批量插入操作
            BulkResponse bulkResponse = restHighLevelClient.bulk(bulkRequest, RequestOptions.DEFAULT);

            // 检查是否有失败的插入操作
            return handleBulkResponse(bulkResponse);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

    }

    private Products handleResponseSourceToEntity(GetResponse response) {
        Map<String, Object> source = response.getSource();
        Products products = new Products();
        products.setProductId(Integer.parseInt(source.get("product_id").toString()));
        products.setProductName(source.get("product_name").toString());
        products.setProductImage(source.get("product_image").toString());
        products.setDescription(source.get("description").toString());
        products.setPrice(source.get("price").toString());
        return products;
    }

    private boolean productsEqual(Products products1, Products products2) {
        return products1.getProductId().equals(products2.getProductId())
                && products1.getProductName().equals(products2.getProductName())
                && products1.getDescription().equals(products2.getDescription())
                && products1.getPrice().equals(products2.getPrice())
                && products1.getProductImage().equals(products2.getProductImage());
    }

    /**
     * 更新单个数据
     *
     * @param data 要更新的数据对象
     * @return 是否更新成功
     */
    public boolean updateData(Object data) {
        try {
            String indexName = "products";
            String id = getObjectId(data);

            UpdateRequest updateRequest = new UpdateRequest(indexName, id)
                    .doc(JSON.toJSONString(data), XContentType.JSON);

            UpdateResponse updateResponse = restHighLevelClient.update(updateRequest, RequestOptions.DEFAULT);

            return handleDocWriteResponse(id, updateResponse);
        } catch (IOException e) {
            log.error("Error during updateData: {}", e.getMessage(), e);
            return false;
        }
    }


    /**
     * 单个删除数据
     *
     * @param id 数据的唯一ID
     * @return 是否操作成功
     */
    public boolean productDeleteById(String id) {
        String indexName = "products";
        try {
            DeleteRequest deleteRequest = new DeleteRequest(indexName, "_doc", id);
            DeleteResponse deleteResponse = restHighLevelClient.delete(deleteRequest, RequestOptions.DEFAULT);
            return handleDocWriteResponse(id, deleteResponse);
        } catch (Exception e) {
            log.error(" delete Exception:{}", e.getMessage(), e);
            return false;
        }
    }

    /**
     * 处理单个文档写入响应
     *
     * @param id       数据的唯一ID
     * @param response 文档写入响应
     * @return 是否操作成功
     */
    private boolean handleDocWriteResponse(String id, DocWriteResponse response) {
        switch (response.getResult()) {
            case CREATED:
                log.info("Data created successfully. ID: {}", id);
                return true;
            case UPDATED:
                log.info("Data updated successfully. ID: {}", id);
                return true;
            case DELETED:
                log.info("Data deleted successfully. ID: {}", id);
                return true;
            case NOOP:
                log.warn("Operation did not change the document. ID: {}", id);
                return false;
            default:
                log.warn("Unexpected result: {}. ID: {}", response.getResult(), id);
                return false;
        }
    }

    /**
     * 处理批量操作响应
     *
     * @param bulkResponse 批量操作响应
     * @return 是否操作成功
     */
    private boolean handleBulkResponse(BulkResponse bulkResponse) {
        if (bulkResponse.hasFailures()) {
            log.error("Bulk operation failed: {}", bulkResponse.buildFailureMessage());
            return false;
        }
        log.info("Bulk operation completed successfully.");
        return true;
    }
}